  
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">















 
 
 
 
 
 
 
  
  


<html>
  <head>
    <script type="text/javascript" language="JavaScript">
    ORIGINAL_PAGE_PATH = "/appengine/articles/gdata.html";
    </script>
    
    
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Retrieving Authenticated Google Data Feeds with Google App Engine - Google App Engine - Google Code</title>
<script type="text/javascript"><!--
(function(){function a(){this.t={};this.tick=function(c){this.t[c]=(new Date).getTime()};this.tick("start")}var b=new a;window.jstiming={Timer:a,load:b};if(window.external&&window.external.pageT)window.jstiming.pt=window.external.pageT;})();

var _tocPath_ = '/appengine/docs/_toc.ezt';
var codesite_token = null;
//--></script>
<link href="../../css/codesite.pack.04102009.css" type="text/css" rel="stylesheet"></link>
<script src="../../js/codesite_head.pack.04102009.js" type="text/javascript"></script>
<script type="text/javascript">CODESITE_CSITimer['load'].tick('bhs');</script>
<link rel="search" type="application/opensearchdescription+xml" title="Google Code" href="http://code.google.com/osd.xml" />

<!--[if IE]><link rel="stylesheet" type="text/css" href="../../css/iehacks.css" /><![endif]-->

    <link href="../../css/semantic_headers.css" rel="stylesheet" type="text/css" />
    <link href="../css/local_extensions.css" rel="stylesheet" type="text/css" />
  </head>

  <body class="gc-documentation">

    
    
    
</a>

<div id="gb">
 <span>
  
    <a id="lang-dropdown" class="dropdown" href="http://code.google.com" onclick="return false;"><img class="globeicon" src="../../images/globe2_small.png"/><span>English</span></a>
  
 </span>
</div>

<div class="gbh" style="left: 0pt;"></div>
<div class="gbh" style="right: 0pt;"></div>


<style type="text/css">
  #gc-topnav h1 {
    padding: 0 0 0 6px;
  }
</style>


<div id="gc-container">
<a name="top"></a>
<div id="skipto">
  <a href="#gc-pagecontent-anchor">Skip to page content</a>
  <a href="#gc-toc-anchor">Skip to main navigation</a>
</div>

<div id="gc-header">
  <div id="logo"><a href="http://code.google.com">
  
  
     <img src="../../images/cleardot.gif" height="1px" width="1px" alt="Google Code Home Page" id="gc-logo-img"/>
  
  
  </a></div>
  <div id="search">
    <div id="searchForm" class="searchForm">
      <form id="cse" action="http://www.google.com/cse" accept-charset="utf-8" class="gsc-search-box" onsubmit="executeGSearch(document.getElementById('gsearchInput').value); return false;">
      <noscript>
      <input type="hidden" name="cref" value="http://code.google.com/cse/googlecode-context.xml"/>
      </noscript>
        <table class="gsc-search-box" cellpadding="0" cellspacing="0">
          <tbody>
            <tr>
              <td class="gsc-input">
                <input id="gsearchInput" type="text" name="q" maxlength="2048" class="gsc-input" autocomplete="off" title="Google Code Search" style="width:345px"/>
              </td>
              <td class="gsc-search-button">
                <div id="cs-searchresults" onclick="event.cancelBubble = true;"></div>
                <input title="Search" id="gsearchButton" class="gsc-search-button" name="sa" value="Search" type="submit"/>
              </td>
            </tr>
            <tr>
              <td colspan="2" class="greytext">e.g. "templates" or "datastore"</td>
            </tr>
          </tbody>
        </table>
      </form>
    </div> <!-- end searchForm -->
  </div> <!-- end search -->
</div> <!-- end gc-header -->


<div id="codesiteContent">

<a name="gc-topnav-anchor"></a>
<div id="gc-topnav">
  <h1>Google App Engine</h1>
  <ul id="articles" class="gc-topnav-tabs">

    <li id="home_link">
      <a href="../index.html" title="Google App Engine home page">Home</a>
    </li>
  
    <li id="docs_link">
      <a href="../docs/index.html" title="Official Google App Engine documentation">Docs</a>
    </li>
  
    <li id="faq_link">
      <a href="../kb/index.html" title="Answers to frequently asked questions about Google App Engine">FAQ</a>
    </li>
  
    <li id="articles_link">
      <a href="index.html" class="selected" title="Focused articles and tutorials for Google App Engine developers">Articles</a>
    </li>
  
    <li>
      <a href="http://googleappengine.blogspot.com/" title="Official Google App Engine blog">Blog</a>
    </li>
  
    <li>
      <a href="../community.html" title="Community home for Google App Engine">Community</a>
    </li>
  
    <li>
      <a href="../terms.html" title="Google App Engine terms of service">Terms</a>
    </li>
  
    <li>
      <a href="../downloads.html" title="Download Google App Engine">Download</a>
    </li>
  

  </ul>
</div> <!-- end gc-topnav -->

    <div class="g-section g-tpl-170">

      <a name="gc-toc-anchor"></a>  
      <div class="g-unit g-first" id="gc-toc">
        <ul>
  <li><a href="../downloads.html">Downloads</a></li>
  <li><a href="http://code.google.com/status/appengine">System Status</a></li>
  <li><a href="http://code.google.com/p/googleappengine/issues/list">Issue Tracker</a></li>
</ul>
<div class="line"></div>
<ul>
  <li><h2>Getting Started</h2>
    <ul>
      <li><a href="../docs/whatisgoogleappengine.html">What Is Google App Engine?</a></li>
      <li><a href="../docs/java/gettingstarted/index.html">Java</a>
        <ul>
              <li><a href="../docs/java/gettingstarted/introduction.html">Introduction</a></li>
    <li><a href="../docs/java/gettingstarted/installing.html">Installing the Java SDK</a></li>
    <li><a href="../docs/java/gettingstarted/creating.html">Creating a Project</a></li>
    <li><a href="../docs/java/gettingstarted/usingusers.html">Using the Users Service</a></li>
    <li><a href="../docs/java/gettingstarted/usingjsps.html">Using JSPs</a></li>
    <li><a href="../docs/java/gettingstarted/usingdatastore.html">Using the Datastore with JDO</a></li>
    <li><a href="../docs/java/gettingstarted/staticfiles.html">Using Static Files</a></li>
    <li><a href="../docs/java/gettingstarted/uploading.html">Uploading Your Application</a></li>

        </ul>
      </li>
      <li><a href="../docs/python/gettingstarted/index.html">Python</a>
        <ul>
            <li><a href="../docs/python/gettingstarted/introduction.html">Introduction</a></li>
  <li><a href="../docs/python/gettingstarted/devenvironment.html">The Development Environment</a></li>
  <li><a href="../docs/python/gettingstarted/helloworld.html">Hello, World!</a></li>
  <li><a href="../docs/python/gettingstarted/usingwebapp.html">Using the webapp Framework</a></li>
  <li><a href="../docs/python/gettingstarted/usingusers.html">Using the Users Service</a></li>
  <li><a href="../docs/python/gettingstarted/handlingforms.html">Handling Forms With webapp</a></li>
  <li><a href="../docs/python/gettingstarted/usingdatastore.html">Using the Datastore</a></li>
  <li><a href="../docs/python/gettingstarted/templates.html">Using Templates</a></li>
  <li><a href="../docs/python/gettingstarted/staticfiles.html">Using Static Files</a></li>
  <li><a href="../docs/python/gettingstarted/uploading.html">Uploading Your Application</a></li>

        </ul>
      </li>
    </ul>
  </li>
</ul>
<div class="line"></div>
<ul>
  <li><h2>Java <sup class="new">Early Look</sup></h2>
    <ul>
          <li><a href="../docs/java/overview.html">Overview</a></li>
    <li><a href="../docs/java/runtime.html">Servlet Environment</a></li>
    <li><a href="../docs/java/datastore/index.html">Storing Data</a>
      <ul>
            <li><a href="../docs/java/datastore/overview.html">Overview</a></li>
    <li><a href="../docs/java/datastore/usingjdo.html">Using JDO</a></li>
    <li><a href="../docs/java/datastore/dataclasses.html">Defining Data Classes</a></li>
    <li><a href="../docs/java/datastore/creatinggettinganddeletingdata.html">Creating, Getting and Deleting Data</a></li>
    <li><a href="../docs/java/datastore/queriesandindexes.html">Queries and Indexes</a></li>
    <li><a href="../docs/java/datastore/transactions.html">Transactions</a></li>
    <li><a href="../docs/java/datastore/relationships.html">Relationships</a></li>
    <li><a href="../docs/java/datastore/usingjpa.html">Using JPA</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/datastore/package-summary.html">Low-level API</a></li>

      </ul>
    </li>
    <li><a href="../docs/java/apis.html">Services</a>
      <ul>
        <li><a href="../docs/java/memcache/index.html">Memcache</a>
          <ul>
                <li><a href="../docs/java/memcache/overview.html">Overview</a></li>
    <li><a href="../docs/java/memcache/usingjcache.html">Using JCache</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/memcache/package-summary.html">Low-level API</a></li>

          </ul>
        </li>
        <li><a href="../docs/java/urlfetch/index.html">URL Fetch</a>
          <ul>
                <li><a href="../docs/java/urlfetch/overview.html">Overview</a></li>
    <li><a href="../docs/java/urlfetch/usingjavanet.html">Using java.net</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/urlfetch/package-summary.html">Low-level API</a></li>

          </ul>
        </li>
        <li><a href="../docs/java/mail/index.html">Mail</a>
          <ul>
                <li><a href="../docs/java/mail/overview.html">Overview</a></li>
    <li><a href="../docs/java/mail/usingjavamail.html">Using JavaMail</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/mail/package-summary.html">Low-level API</a></li>

          </ul>
        </li>
        <li><a href="../docs/java/images/index.html">Images</a>
          <ul>
                <li><a href="../docs/java/images/overview.html">Overview</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/images/package-summary.html">API Reference</a></li>

          </ul>
        </li>
        <li><a href="../docs/java/users/index.html">Google Accounts</a>
          <ul>
                <li><a href="../docs/java/users/overview.html">Overview</a></li>
    <li><a href="../docs/java/javadoc/com/google/appengine/api/users/package-summary.html">API Reference</a></li>

          </ul>
        </li>
      </ul>
    </li>
    <li><a href="../docs/java/javadoc/index.html">Javadoc Reference</a></li>
    <li><a href="../docs/java/jrewhitelist.html">JRE Class White List</a></li>
    <li><a href="../docs/java/config/index.html">Configuration</a>
      <ul>
            <li><a href="../docs/java/config/webxml.html">Deployment Descriptor</a></li>
    <li><a href="../docs/java/config/appconfig.html">App Config</a></li>
    <li><a href="../docs/java/config/indexconfig.html">Index Config</a></li>
    <li><a href="../docs/java/config/cron.html">Scheduled Tasks</a></li>

      </ul>
    </li>
    <li><a href="../docs/java/tools/index.html">Tools</a>
      <ul>
            <li><a href="../docs/java/tools/devserver.html">Development Server</a></li>
    <li><a href="../docs/java/tools/uploadinganapp.html">Uploading and Managing</a></li>
    <li><a href="../docs/java/tools/eclipse.html">Google Plugin for Eclipse</a></li>
    <li><a href="../docs/java/tools/ant.html">Using Apache Ant</a></li>

      </ul>
    </li>
    <li><a href="../docs/java/howto/index.html">How-To</a>
      <ul>
              <li><a href="../docs/java/howto/unittesting.html">Unit Testing</a></li>

      </ul>
    </li>

    </ul>
  </li>
</ul>
<div class="line"></div>
<ul>
  <li><h2>Python</h2>
    <ul>
          <li><a href="../docs/python/overview.html">Overview</a></li>
    <li><a href="../docs/python/runtime.html">CGI Environment</a></li>
    <li><a href="../docs/python/datastore/index.html">Storing Data</a>
      <ul>
             <li><a href="../docs/python/datastore/overview.html">Overview</a></li>
     <li><a href="../docs/python/datastore/entitiesandmodels.html">Entities and Models</a></li>
     <li><a href="../docs/python/datastore/creatinggettinganddeletingdata.html">Creating, Getting and Deleting Data</a></li>
     <li><a href="../docs/python/datastore/keysandentitygroups.html">Keys and Entity Groups</a></li>
     <li><a href="../docs/python/datastore/queriesandindexes.html">Queries and Indexes</a></li>
     <li><a href="../docs/python/datastore/transactions.html">Transactions</a></li>
     <li><a href="../docs/python/datastore/typesandpropertyclasses.html">Types and Property Classes</a></li>
     <li><a href="../docs/python/datastore/gqlreference.html">GQL Reference</a></li>

     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/datastore/modelclass.html">Model</a></li>
         <li><a href="../docs/python/datastore/expandoclass.html">Expando</a></li>
         <li><a href="../docs/python/datastore/polymodelclass.html">PolyModel</a></li>
         <li><a href="../docs/python/datastore/propertyclass.html">Property</a></li>
         <li><a href="../docs/python/datastore/queryclass.html">Query</a></li>
         <li><a href="../docs/python/datastore/gqlqueryclass.html">GqlQuery</a></li>
         <li><a href="../docs/python/datastore/keyclass.html">Key</a></li>
         <li><a href="../docs/python/datastore/functions.html">Functions</a></li>
         <li><a href="../docs/python/datastore/exceptions.html">Exceptions</a></li>
       </ul>
     </li>

      </ul>
    </li>
    <li><a href="../docs/python/apis.html">Services</a>
      <ul>
        <li><a href="../docs/python/memcache/index.html">Memcache</a>
          <ul>
                 <li><a href="../docs/python/memcache/overview.html">Overview</a></li>
      <li><a href="../docs/python/memcache/usingmemcache.html">Using Memcache</a></li>
     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/memcache/clientclass.html">Client</a></li>
         <li><a href="../docs/python/memcache/functions.html">Functions</a></li>
       </ul>
     </li>

          </ul>
        </li>
        <li><a href="../docs/python/urlfetch/index.html">URL Fetch</a>
          <ul>
                 <li><a href="../docs/python/urlfetch/overview.html">Overview</a></li>
     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/urlfetch/fetchfunction.html">The fetch Function</a></li>
         <li><a href="../docs/python/urlfetch/responseobjects.html">Response Objects</a></li>
         <li><a href="../docs/python/urlfetch/exceptions.html">Exceptions</a></li>
       </ul>
     </li>

          </ul>
        </li>
        <li><a href="../docs/python/mail/index.html">Mail</a>
          <ul>
                 <li><a href="../docs/python/mail/overview.html">Overview</a></li>
     <li><a href="../docs/python/mail/sendingmail.html">Sending Mail</a></li>
     <li><a href="../docs/python/mail/attachments.html">Attachments</a></li>
     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/mail/emailmessageclass.html">EmailMessage</a></li>
         <li><a href="../docs/python/mail/emailmessagefields.html">Message Fields</a></li>
         <li><a href="../docs/python/mail/functions.html">Functions</a></li>
         <li><a href="../docs/python/mail/exceptions.html">Exceptions</a></li>
       </ul>
     </li>

          </ul>
        </li>
        <li><a href="../docs/python/images/index.html">Images</a>
          <ul>
                 <li><a href="../docs/python/images/overview.html">Overview</a></li>
     <li><a href="../docs/python/images/installingPIL.html">Installing PIL</a></li>
     <li><a href="../docs/python/images/usingimages.html">Using the Images API</a></li>
     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/images/imageclass.html">Image</a></li>
         <li><a href="../docs/python/images/functions.html">Functions</a></li>
         <li><a href="../docs/python/images/exceptions.html">Exceptions</a></li>
       </ul>
     </li>

          </ul>
        </li>
        <li><a href="../docs/python/users/index.html">Google Accounts</a>
          <ul>
                 <li><a href="../docs/python/users/overview.html">Overview</a></li>
     <li><a href="../docs/python/users/userobjects.html">User Objects</a></li>
     <li><a href="../docs/python/users/loginurls.html">Login URLs</a></li>
     <li><a href="../docs/python/users/adminusers.html">Admin Users</a></li>

     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/users/userclass.html">User</a></li>
         <li><a href="../docs/python/users/functions.html">Functions</a></li>
         <li><a href="../docs/python/users/exceptions.html">Exceptions</a></li>
       </ul>
     </li>

          </ul>
        </li>
      </ul>
    </li>
    <li><a href="../docs/python/config/index.html">Configuration</a>
      <ul>
            <li><a href="../docs/python/config/appconfig.html">App Config</a></li>
    <li><a href="../docs/python/config/indexconfig.html">Index Config</a></li>
    <li><a href="../docs/python/config/cron.html">Scheduled Tasks</a></li>

      </ul>
    </li>
    <li><a href="../docs/python/tools/index.html">Tools</a>
      <ul>
            <li><a href="../docs/python/tools/devserver.html">Development Server</a></li>
    <li><a href="../docs/python/tools/uploadinganapp.html">Uploading and Managing</a></li>
    <li><a href="../docs/python/tools/uploadingdata.html">Uploading Data</a></li>
    <li><a href="../docs/python/tools/webapp/index.html">webapp Framework</a>
      <ul>
             <li><a href="../docs/python/tools/webapp/overview.html">Overview</a></li>
     <li><a href="../docs/python/tools/webapp/running.html">Running the Application</a></li>
     <li><a href="../docs/python/tools/webapp/requesthandlers.html">Request Handlers</a></li>
     <li><a href="../docs/python/tools/webapp/requestdata.html">Request Data</a></li>
     <li><a href="../docs/python/tools/webapp/buildingtheresponse.html">Building the Response</a></li>
     <li><a href="../docs/python/tools/webapp/redirects.html">Redirects, Headers and Status Codes</a></li>
     

     <li><span class="tlw-title tlw-expanded">Reference</span>
       <ul>
         <li><a href="../docs/python/tools/webapp/requestclass.html">Request</a></li>
         <li><a href="../docs/python/tools/webapp/responseclass.html">Response</a></li>
         <li><a href="../docs/python/tools/webapp/requesthandlerclass.html">RequestHandler</a></li>
         <li><a href="../docs/python/tools/webapp/wsgiapplicationclass.html">WSGIApplication</a></li>
         <li><a href="../docs/python/tools/webapp/utilmodule.html">Utility Functions</a></li>
         
       </ul>
     </li>

      </ul>
    </li>
    <li><a href="../docs/python/tools/libraries.html">Third-party Libraries</a></li>

      </ul>
    </li>
    <li><a href="../docs/python/howto/index.html">How-To</a>
      <ul>
              <li><a href="../docs/python/howto/usinggdataservices.html">Google Data Services</a></li>

      </ul>
    </li>

    </ul>
  </li>
</ul>
<div class="line"></div>
<ul>
  <li><h2>Managing Your App</h2>
    <ul>
      <li><a href="../docs/theadminconsole.html">The Admin Console</a></li>
      <li><a href="../docs/quotas.html">Quotas</a></li>
      <li><a href="../docs/billing.html">Billing</a></li>
    </ul>
  </li>
</ul>
<div class="line"></div>
<ul>
  <li><h2>Resources</h2>
    <ul>
      <li><a href="../kb/index.html">FAQ</a></li>
      <li><a href="index.html">Articles</a></li>
      <li><a href="http://appengine-cookbook.appspot.com/">Cookbook</a></li>
      <li><a href="http://appgallery.appspot.com/">App Gallery</a></li>
      <li><a href="http://code.google.com/p/googleappengine/">SDK Code</a></li>
      <li><a href="http://code.google.com/p/google-app-engine-samples/">Sample Apps Code</a></li>
      <li><a href="../community.html">Discussion Groups</a></li>
    </ul>
  </li>
</ul>
<div class="line"></div>
<ul>
  <li><a href="../docs/roadmap.html">Product Roadmap</a></li>
  <li><a href="http://code.google.com/p/googleappengine/wiki/SdkReleaseNotes">Release Notes</a></li>
  <li><a href="../docs/revision_history.html">Revision History</a></li>
</ul>

        <a class="hidden" href="#gc-topnav-anchor">More Google App Engine resource links</a>
      </div>
      
      <a name="gc-pagecontent-anchor"></a>   
      <div class="g-unit" id="gc-pagecontent">
        <script type="text/javascript">CODESITE_docEarlyProcessing();</script>
        <h1 class="page_title">Retrieving Authenticated Google Data Feeds with Google App Engine</h1>


<div id="jd-content">
<div class="jd-descr">
<i>Jeff Scudder, Google Developer Programs</i><br>
<i>April 17, 2008, updated October 22, 2008</i>

<h2>Introduction</h2>

<p>I'm sure your mind is positively buzzing with ideas for how to use Google App Engine, and a few of you might be interested in building an app that interacts with some of Google's other services via our <a href="http://code.google.com/apis/gdata/overview.html">Google Data</a> AtomPub APIs.  Quite of few of Google's products expose a Google Data API, (a few interesting examples are YouTube, Google Calendar, and Blogger--you can find a complete <a href="http://code.google.com/apis/gdata/">list here</a>) and these APIs can be used to read and edit the user-specific data they expose.</p>

<p>In this article we'll use the Google Documents List Data API to walk through the process of requesting access from and retrieving data for a particular user.  We'll use Google App Engine's <a href="http://code.google.com/appengine/docs/python/tools/webapp/">webapp framework</a> to generate the application pages, and the <a href="http://code.google.com/appengine/docs/python/users/">Users API</a> to authenticate users with Google Accounts.</p>

<h2>Google's AuthSub APIs</h2>

<p>Some Google Data services require authorization from your users to read data, and all Google Data services require their authorization before your app can write to these services on the user's behalf. Google uses <a href="http://code.google.com/apis/accounts/docs/AuthSub.html">AuthSub</a> to enable users to authorize your app to access specific services.</p>

<p>Using AuthSub, users type their password into a secure page at google.com, then are redirected back to your app. Your app receives a token allowing it to access the requested service until the user revokes the token through the <a href="https://www.google.com/accounts/ManageAccount">Account Management</a> page.</p>

<p>In this article, we'll walk through the process of setting up the login link for the user, obtaining a session token to use for multiple requests to Google Data services, and storing the token in the datastore so that it can be reused for returning users.</p>

<a name="configure"></a>
<h2>Using the gdata-python-client library</h2>

<p>Google offers a Google Data Python client library that simplifies token management and requesting data from specific Google Data APIs.  We recently released a <a href="http://googledataapis.blogspot.com/2008/04/release-hounds-support-for-app-engine.html">version of this library</a> that supports making requests from Google App Engine applications.  In this article we'll use this library, but of course you're welcome to use whatever works best for your application.  <a href="http://code.google.com/p/gdata-python-client">Download the gdata-python-client library</a>.</p>

<p>To use this library with your Google App Engine application, simply place the library source files in your application's directory, and import them as you usually would. The source directories you need to upload with your application code are <code>src/gdata</code> and <code>src/atom</code>. Then, be sure to call the <code>gdata.alt.appengine.run_on_appengine</code> function on each instance of a <code>gdata.service.GDataService</code> object. There's nothing more to it than that!</p>

<h2>Step 1: Generating the Token Request Link</h2>

<p>Applications use an API called <a href="http://code.google.com/apis/accounts/docs/AuthForWebApps.html">AuthSub</a> to obtain a user's permission for accessing protected Google Data feeds. The process is fairly simple. To request access from a user to a protected feed, your app will redirect the user to a secure page on google.com where the user can sign in to grant or deny access.  Once doing so, the user is then redirected back to your app with the newly-granted token stored in the URL.<p>

<p>Your application needs to specify two things when using AuthSub: the common base URL for the feeds you want to access, and the redirect URL for your app, where the user will be sent after authorizing your application.</p>

<p>To generate the token request URL, we'll use the <code>gdata.service</code> module included in the Google Data client library.  This module contains a method, <code>GenerateAuthSubURL</code>, which automatically generates the correct URL given the base feed URL and your website's return address. In the code snippet below, we use this method to generate a URL requesting access to a user's Google Document List feed.</p>

<p>In our <code>app.yaml</code> file, we will define a URL mapping to create a separate URL for each step. Here's an example:</p>

<pre>application: gdata-feedfetcher
version: 1
runtime: python
api_version: 1

handlers:
- url: /step1.*
  script: step1.py

- url: /step2.*
  script: step2.py

...</pre>

<p>This sample application also includes a <code>settings</code> module to allow you to easily change the host name for your app. By default the <code>settings</code> module attempts to detect the current server settings.</p>

<pre class="prettyprint">import os


port = os.environ['SERVER_PORT']
if port and port != '80':
  HOST_NAME = '%s:%s' % (os.environ['SERVER_NAME'], port)
else:
  HOST_NAME = os.environ['SERVER_NAME']</pre>

<p>To illustrate this first step of using AuthSub in the app, we will create a step1.py script that looks something like this:</p>

<pre class="prettyprint">import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import users
import atom.url
import gdata.service
import gdata.alt.appengine
import settings


class Fetcher(webapp.RequestHandler):

  def get(self):
    next_url = atom.url.Url('http', settings.HOST_NAME, path='/step1')

    # Initialize a client to talk to Google Data API services.
    client = gdata.service.GDataService()
    gdata.alt.appengine.run_on_appengine(client)

    # Generate the AuthSub URL and write a page that includes the link
    self.response.out.write("""&lt;html&gt;&lt;body&gt;
        &lt;a href="%s"&gt;Request token for the Google Documents Scope&lt;/a&gt;
        &lt;/body&gt;&lt;/html&gt;""" % client.GenerateAuthSubURL(next_url,
            ('http://docs.google.com/feeds/',), secure=False, session=True))


def main():
  application = webapp.WSGIApplication([('/.*', Fetcher),], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()</pre>

<p>In this example, the first URL passed to <code>GenerateAuthSubURL</code> returns the user to our application, and the second is the Google Documents List feed base URL, which indicates which service our app is requesting authorization for. After you click the link and authorize your application, you will be taken back to the same original page but the single use AuthSub token will now be a URL parameter in the appspot URL.</p>

<h2>Step 2: Retrieving and Updating a Token</h2>

<p>Once we've generated an authorization request URL for a particular Google Data service, we'll need a way to use the token returned to our app to access the feed in question.  Now, we need to retrieve the initial token returned to us for the Google Documents List API, and upgrade that token to a permanent session token.  Remember that we told the service to redirect the user to the URL 'http://gdata-feedfetcher.appspot.com/step1'.  Let's extend our simple example above to do a few things. We'll call this new version <code>step2</code>.</p>

<p>Let's write the functionality that will handle the return request from the Google Data service the user signed in to.  The Google Data service will request a URL that will look something like this:</p>

<p><code>http://gdata-feedfetcher.appspot.com/?auth_sub_scopes=http%3A%2F%2Fdocs.google.com%2Ffeeds%2F&<b>token=CKC5y...MgH</b></code></p>

<p>Which is just our return URL appended with the initial authorization token for the service which grants our app access for our user.  The code below first takes this URL and extracts the service and the token.  Then, it requests an upgrade for the token for the document list service.</p>

<p>We use two new methods to achieve this.  First, we try to obtain the single use AuthSub token by examining the current page's URL. The <code>gdata.auth.extract_auth_sub_token_from_url</code> function handles token extraction for us. To upgrade this initial token to a session token, we use <code>client.upgrade_to_session_token(auth_token)</code>.</p>

<p>Now that we have a session token which grants our app access to the user's Google Documents, our app needs to decide if this token should be stored in the datastore for future use. If the user is signed in to our app, we can associate this AuthSub token with the current user and store it, so that the user will not need to repeat the authorization process the next time that they use our app. If our app does not know who the current user is, we should not store the token.</p>

<p>The code for making this decision and telling the Google Data client how to use the token looks like this:</p>

<pre>if session_token and users.get_current_user():
  client.token_store.add_token(session_token)
elif session_token:
  client.current_token = session_token</pre>

<p>Below is the code for <code>step2.py</code> which illustrates upgrading to a session token and storing it for future use if there is a signed-in user.</p>

<p class="note"><strong>Note:</strong> With Google App Engine, you must use the URLFetch API to request external URLs.  In our Google Data Python client library, <code>gdata.service</code> does not use the URLFetch API by default.  We have to tell the service object to use URLFetch by calling <code>gdata.alt.appengine.run_on_appengine</code> on the service object, like this: <code>gdata.alt.appengine.run_on_appengine(client)</code></p>

<pre class="prettyprint">import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import users
import atom.url
import gdata.service
import gdata.alt.appengine
import settings


class Fetcher(webapp.RequestHandler):

  def get(self):
    # Write our pages title
    self.response.out.write("""&lt;html&gt;&lt;head&gt;&lt;title&gt;
        Google Data Feed Fetcher: read Google Data API Atom feeds&lt;/title&gt;""")
    self.response.out.write('&lt;/head&gt;&lt;body&gt;')
    # Allow the user to sign in or sign out
    next_url = atom.url.Url('http', settings.HOST_NAME, path='/step2')
    if users.get_current_user():
      self.response.out.write('&lt;a href="%s"&gt;Sign Out&lt;/a&gt;&lt;br&gt;' % (
          users.create_logout_url(str(next_url))))
    else:
      self.response.out.write('&lt;a href="%s"&gt;Sign In&lt;/a&gt;&lt;br&gt;' % (
          users.create_login_url(str(next_url))))

    # Initialize a client to talk to Google Data API services.
    client = gdata.service.GDataService()
    gdata.alt.appengine.run_on_appengine(client)

    session_token = None
    # Find the AuthSub token and upgrade it to a session token.
    auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
    if auth_token:
      # Upgrade the single-use AuthSub token to a multi-use session token.
      session_token = client.upgrade_to_session_token(auth_token)
    if session_token and users.get_current_user():
      # If there is a current user, store the token in the datastore and
      # associate it with the current user. Since we told the client to
      # run_on_appengine, the add_token call will automatically store the
      # session token if there is a current_user.
      client.token_store.add_token(session_token)
    elif session_token:
      # Since there is no current user, we will put the session token
      # in a property of the client. We will not store the token in the
      # datastore, since we wouldn't know which user it belongs to.
      # Since a new client object is created with each get call, we don't
      # need to worry about the anonymous token being used by other users.
      client.current_token = session_token

    self.response.out.write('&lt;div id="main"&gt;&lt;/div&gt;')
    self.response.out.write(
        '&lt;div id="sidebar"&gt;&lt;div id="scopes"&gt;&lt;h4&gt;Request a token&lt;/h4&gt;&lt;ul&gt;')
    self.response.out.write('&lt;li&gt;&lt;a href="%s">Google Documents&lt;/a&gt;&lt;/li&gt;' % (
        client.GenerateAuthSubURL(
            next_url,
            ('http://docs.google.com/feeds/',), secure=False, session=True)))
    self.response.out.write('&lt;/ul&gt;&lt;/div&gt;&lt;br/&gt;&lt;div id="tokens"&gt;')


def main():
  application = webapp.WSGIApplication([('/.*', Fetcher),], debug=True)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()</pre>

<p>After we upgrade the initial token using the <code>GDataService.upgrade_to_session_token</code> method, we can associate the session token with the current user and store it in the datastore for future reuse by calling <code>client.token_store.add_token(session_token)</code>. If we do not store the AuthSub session token, the user will need to perform the AuthSub authorization redirects each time that they use our application. If there is no current user, the session token will not be stored, since we don't know who the token belongs to. Below, we will take you through the steps to use this token and fetch your user's feed in your application.</p>

<h2>Step 3: Using a session token and fetching a data feed.</h2>

<p>Now that we have obtained and stored the session token, we can use the AuthSub session token to retrieve the user's document list feed with our application. The final step is to get the user feed from Google Docs and display it on our site!</p>

<p>Lets add a new method to our app to request the feed and handle a token required message. We will call this method <code>fetch_feed</code> and add it to the <code>Fetcher</code> request handler class. In this example, the app uses <code>client.Get</code> to try to read data from the feed.</p>

<p>Some Google Data feeds require authorization before they can be read. If our app had previously saved an AuthSub session token for the current user and the desired feed URL, then the token will be found automatically by the client object and used in the request. If we did not have a stored token for the combination of the current user and the desired feed, then we will attempt to fetch the feed anyway. If we receive a "token required" message from the server, then we will ask the user to authorize this app which will give our app a new AuthSub token.</p>

<p>Here is the code for the <code>fetch_feed</code> method:</p>

<pre class="prettyprint">  def fetch_feed(self, client, feed_url):
    # Attempt to fetch the feed.
    if not feed_url:
      self.response.out.write(
          'No feed_url was specified for the app to fetch.&lt;br/&gt;')
      example_url = atom.url.Url('http', settings.HOST_NAME, path='/step3',
          params={'feed_url':
              'http://docs.google.com/feeds/documents/private/full'}
          ).to_string()
      self.response.out.write('Here\'s an example query which will show the'
          ' XML for the feed listing your Google Documents &lt;a '
          'href="%s"&gt;%s&lt;/a&gt;' % (example_url, example_url))
      return
    try:
      response = client.Get(feed_url, converter=str)
      self.response.out.write(cgi.escape(response))
    except gdata.service.RequestError, request_error:
      # If fetching fails, then tell the user that they need to login to
      # authorize this app by logging in at the following URL.
      if request_error[0]['status'] == 401:
        # Get the URL of the current page so that our AuthSub request will
        # send the user back to here.
        next = atom.url.Url('http', settings.HOST_NAME, path='/step3',
          params={'feed_url': feed_url})
        # If there is a current user, we can request a session token, otherwise
        # we should ask for a single use token.
        auth_sub_url = client.GenerateAuthSubURL(next, feed_url,
            secure=False, session=True)
        self.response.out.write('&lt;a href="%s"&gt;' % (auth_sub_url))
        self.response.out.write(
            'Click here to authorize this application to view the feed&lt;/a&gt;')
      else:
        self.response.out.write(
            'Something went wrong, here is the error object: %s ' % (
                str(request_error[0])))</pre>

<p>The above method uses the <code>cgi</code> module, so be sure to add <code>import cgi</code> to the list of imports at the beginning of your script.</p>

<p>Now that we have a method to fetch the target feed, we can modify the <code>Fetcher</code> class' <code>get</code> method to call this method after we upgrade and store the AuthSub token.</p>

<p>Our app also needs to know the URL which should be fetched, so we add a URL parameter to the incoming request to indicate which feed should be fetched. The below code for the <code>get</code> method adds the ability to find out which URL the app should fetch and fetches the desired feed.</p>

<pre class="prettyprint">import wsgiref.handlers
import cgi
from google.appengine.ext import webapp
from google.appengine.api import users
import atom.url
import gdata.service
import gdata.alt.appengine
import settings


class Fetcher(webapp.RequestHandler):

  def get(self):
    # Write our pages title
    self.response.out.write("""&lt;html>&lt;head&gt;&lt;title&gt;
        Google Data Feed Fetcher: read Google Data API Atom feeds&lt;/title&gt;""")
    self.response.out.write('&lt;/head&gt;&lt;body&gt;')
    next_url = atom.url.Url('http', settings.HOST_NAME, path='/step3')
    # Allow the user to sign in or sign out
    if users.get_current_user():
      self.response.out.write('&lt;a href="%s"&gt;Sign Out&lt;/a&gt;&lt;br&gt;' % (
          users.create_logout_url(str(next_url))))
    else:
      self.response.out.write('&lt;a href="%s"&gt;Sign In&lt;/a&gt;&lt;br&gt;' % (
          users.create_login_url(str(next_url))))

    # Initialize a client to talk to Google Data API services.
    client = gdata.service.GDataService()
    gdata.alt.appengine.run_on_appengine(client)

    feed_url = self.request.get('feed_url')

    session_token = None
    # Find the AuthSub token and upgrade it to a session token.
    auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
    if auth_token:
      # Upgrade the single-use AuthSub token to a multi-use session token.
      session_token = client.upgrade_to_session_token(auth_token)
    if session_token and users.get_current_user():
      # If there is a current user, store the token in the datastore and
      # associate it with the current user. Since we told the client to
      # run_on_appengine, the add_token call will automatically store the
      # session token if there is a current_user.
      client.token_store.add_token(session_token)
    elif session_token:
      # Since there is no current user, we will put the session token
      # in a property of the client. We will not store the token in the
      # datastore, since we wouldn't know which user it belongs to.
      # Since a new client object is created with each get call, we don't
      # need to worry about the anonymous token being used by other users.
      client.current_token = session_token

    self.response.out.write('&lt;div id="main"&gt;')
    self.fetch_feed(client, feed_url)
    self.response.out.write('&lt;/div&gt;')
    self.response.out.write(
        '&lt;div id="sidebar">&lt;div id="scopes"&gt;&lt;h4&gt;Request a token&lt;/h4&gt;&lt;ul&gt;')
    self.response.out.write('&lt;li&gt;&lt;a href="%s"&gt;Google Documents&lt;/a&gt;&lt;/li&gt;' % (
        client.GenerateAuthSubURL(
            next_url,
            ('http://docs.google.com/feeds/',), secure=False, session=True)))
    self.response.out.write('&lt;/ul&gt;&lt;/div&gt;&lt;br/&gt;&lt;div id="tokens"&gt;')</pre>

<p>In the above, we request the feed by calling <code>self.fetch_feed(client, feed_url)</code>.</p>

<p>You can see the final program at work by visiting: <a href="http://gdata-feedfetcher.appspot.com/">http://gdata-feedfetcher.appspot.com/</a>.  Also, view the complete source code, where we put all of this together at the <a href="http://code.google.com/p/google-app-engine-samples">Google App Engine sample code project</a> on Google Code Hosting.</p>

<p>The AuthSub session tokens are long lived, but they can be revoked by the user or by your application. At some point, a session token stored in your data store may become revoked so your application should handle cleanup of tokens which can no longer be used. The status of a token can be tested by <a href="http://code.google.com/apis/accounts/docs/AuthForWebApps.html#AuthSubTokenInfo">querying the token info URL</a>. You can read more about AuthSub token management in the <a href="http://code.google.com/apis/accounts/docs/AuthForWebApps.html">AuthSub documentation</a>. This feature is left as an exercise to the reader, have fun :)</p>

<h2>Conclusion</h2>

<p>Using the Google Data Python client library, you can easily manage your user's Google Data feeds in your own Google App Engine application.</p>

<p>The Google Data Python client library includes support for almost all of the Google Data services.  For further information, you can read the <a href="http://code.google.com/apis/gdata/articles/python_client_lib.html">getting started guide</a> for the library, visit the <a href="http://code.google.com/p/gdata-python-client/">project</a> to browse the source, and even ask questions on the <a href="http://groups.google.com/group/gdata-python-client-library-contributors">gdata-python-client's Google group</a>.</p>

<p>As always, for questions about Google App Engine, read our <a href="http://code.google.com/appengine/docs">online documentation</a> and visit our <a href="http://groups.google.com/group/google-appengine">google group</a>.</p>

<h2>Appendix: ClientLogin</h2>

<p>This example uses AuthSub to authorize the app to act on the user's bahalf, but the Google Data APIs support other authorization mechanisms. In some cases, you might want to temporarily use <a href="http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html">ClientLogin</a> while developing your application. If you are going to use ClientLogin, you'll need to add a couple of parameters to the <code>run_on_appengine</code> command:</p>

<pre class="prettyprint">client = gdata.service.GDataService()
# Tell the client that we are running in single user mode, and it should not
# automatically try to associate the token with the current user then store
# it in the datastore.
gdata.alt.appengine.run_on_appengine(client, store_tokens=False, 
    single_user_mode=True)
client.email = 'app_account_username@example.com'
client.password = 'password'
# To request a ClientLogin token you must specify the desired service using
# its service name.
client.service = 'blogger'
# Request a ClientLogin token, which will be placed in the client's 
# current_token member.
client.ProgrammaticLogin()</pre>

<p>You may receive a CAPTCHA challenge when requesting a ClientLogin token which you will need to handle in your app before you can receive a ClientLogin token. For this and a few other reasons, I don't recommend using ClientLogin in Google App Engine, but the above is how you could use it while developing your app.</p>

</div>
</div>
   
      </div><!-- end gc-pagecontent -->
   </div><!-- end gooey wrapper -->

    </div> <!-- end codesite content -->

<div id="gc-footer" dir="ltr">
  <div class="text">
    
      <div class="notice"><div id="notice" style="text-align: center; border: 1em 0em 1em 0em">
  Except as otherwise <a
  href="http://code.google.com/policies.html#restrictions">noted</a>,
  the content of this page is licensed under the <a rel="license"
  href="http://creativecommons.org/licenses/by/2.5/">Creative Commons
  Attribution 2.5 License</a>, and code samples are licensed under the
  <a rel="license" href="http://www.apache.org/licenses/LICENSE-2.0">Apache
  2.0 License</a>.
<!-- <rdf:RDF xmlns="http://web.resource.org/cc/" 
              xmlns:dc="http://purl.org/dc/elements/1.1/"
              xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <Work rdf:about="">
    <license rdf:resource="http://creativecommons.org/licenses/by/2.5/" />
  </Work>
  <License rdf:about="http://creativecommons.org/licenses/by/2.5/">
    <permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
    <permits rdf:resource="http://web.resource.org/cc/Distribution"/>
    <requires rdf:resource="http://web.resource.org/cc/Notice"/>
    <requires rdf:resource="http://web.resource.org/cc/Attribution"/>
    <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
  </License>
</rdf:RDF> -->
</div>
Java is a registered trademark of Sun Microsystems, Inc.</div>
    
    &copy;2009 Google -
    <a href="http://code.google.com">Code Home</a> -
    <a href="http://www.google.com/accounts/TOS">Terms of Service</a> -
    <a href="http://www.google.com/privacy.html">Privacy Policy</a> -
    <a href="http://code.google.com/more">Site Directory</a>
    <br /> <br />
    Google Code offered in:
    <a href="http://code.google.com/intl/en">English</a> -
    <a href="http://code.google.com/intl/es">Español</a> -
    <a href="http://code.google.com/intl/ja">日本語</a> -
    <a href="http://code.google.com/intl/ko">한국어</a> -
    <a href="http://code.google.com/intl/pt-BR">Português</a> -
    <a href="http://code.google.com/intl/ru">Pусский</a> -
    <a href="http://code.google.com/intl/zh-CN">中文(简体)</a> -
    <a href="http://code.google.com/intl/zh-TW">中文(繁體)</a>
  </div>
</div><!-- end gc-footer -->

</div><!-- end gc-containter -->

<script type="text/javascript">CODESITE_CSITimer['load'].tick('ats');</script>
<script src="../../js/codesite_tail.pack.04102009.js" type="text/javascript"></script>






  </body>
</html>


